
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/multi.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include "list.h"
#include "log.h"
#include "types.h"
#include "enprodev.h"
#include "util.h"




// static char HttpURL[128] = { 0 };
// static char HttpSpace[64] = { 0 };
// static struct curl_slist *Headers = NULL;
// static YapiParser* EnvproStrParser = NULL;

typedef struct _tag_env_pro_priv_msg_t 
{
    struct list_head head;
    struct list_head* current;
    int complete;
    char* nTime;
    char* aTime;
    char* httpurl;
    char* space;
    struct curl_slist *header;
    YapiParser* parser;
    pthread_cond_t pcond;
    pthread_mutex_t pmutex;
} EPPrivMsg;

static int _enprodev_init(EnvProDev* dev, YapiSerConfig* yapiserconf, YapiParser* parser)
{
    int ret = dev && dev->msg && yapiserconf && parser;
    //printf("envprodev addr %p\n", dev);
    if ( ret )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;

        sprintf(epmsg->httpurl, "http://%s:%s/%s", yapiserconf->ip, yapiserconf->port, yapiserconf->index);
        printf("epmsg->httpurl: %s\n", epmsg->httpurl);
        strcpy(epmsg->space, yapiserconf->space);
        //printf("space is %s\n", dev->space);
        epmsg->header = curl_slist_append(epmsg->header, "Content-Type: application/json");
        epmsg->parser = parser;
    }

    return ret;
}

static void _read_envprodev_pointcode(char* *envprodev, CsvParser* parser)
{
    if ( parser )
    {
        int i = 0;
        CsvNode* node = NULL;
        for (csvparser_begin(parser); !csvparser_is_end(parser); csvparser_next(parser), ++i)
        {
            node = csvparser_current(parser);
            *(envprodev+i) = node->code;
        }
    }

    // printf("_read_envprodev_pointcode\n");
}


static void _setup_curl_handle(EPPrivMsg* msg, CURL* handle, const char* data, struct list_head* list)
{
    curl_easy_setopt(handle, CURLOPT_URL, msg->httpurl);
    curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(handle, CURLOPT_DEFAULT_PROTOCOL, "http");
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, msg->parser->analysis);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, list);
    curl_easy_setopt(handle, CURLOPT_HTTPHEADER, msg->header);
    curl_easy_setopt(handle, CURLOPT_POSTFIELDS, data);
    // printf("url: %s\n", msg->httpurl);
    // printf("list: %p\n", list);
    // printf("header: %p\n", msg->header);
    // printf("analysis: %p\n", msg->parser->analysis);
    // printf("%s\n", data);
}

static void _local_time(char* ntime, char* atime)
{
    time_t new_time;
    time(&new_time);
    new_time -= 60;
    get_local_time(new_time, ntime);
    get_local_ago_time(new_time, atime);
}

static void _send_cond(EPPrivMsg* msg)
{
    msg->complete = 1;
    pthread_cond_signal(&msg->pcond);
}

static const char *CodeStr = "{\r\n\"code\":\"";
static const char *Code_Startime = "\",\r\n\"startTime\":\"";
static const char *Start_Endtime = "\",\r\n\"endTime\":\"";
static const char *End_Space = "\",\r\n\"space\":\"";
static const char *End_Values = "\",\r\n\"values\":[\"mean\"]\r\n}";
static int _enprodev_request_datat(EnvProDev* dev, CsvParser* parser)
{
    int dev_count = csvparser_get_length(parser);
    int success = 1;

    if ( (dev_count > 0) && dev )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        char* DevPnameStr[dev_count];
        CURL *handles[dev_count];
        CURLM *multi_handle;
        int running;

        _read_envprodev_pointcode(DevPnameStr, parser);
        curl_global_init(CURL_GLOBAL_ALL);
        multi_handle = curl_multi_init();
        char ntime[32];
        char atime[32];
        _local_time(ntime, atime);
        //printf("ntime is %s\n", ntime);
        //printf("atime is %s\n", atime);
        if ( epmsg->nTime && epmsg->aTime )
        {
            strcpy(epmsg->nTime, ntime);
            strcpy(epmsg->aTime, atime);
        }
        else
        {
            printf("nTime or aTime is NULL\n");
            
        }
        char* BodyStr[dev_count];
        //printf("start foreach %d\n", dev_count);
        for (int i = 0; i < dev_count; i++) 
        {
            BodyStr[i] = (char*)malloc(200);
            if ( BodyStr[i] )
            {
                handles[i] = curl_easy_init();
                sprintf(BodyStr[i], "%s%s%s%s%s%s%s%s%s", CodeStr, DevPnameStr[i], \
                    Code_Startime, atime, Start_Endtime, ntime, End_Space, \
                    epmsg->space, End_Values);
             
                _setup_curl_handle(epmsg, handles[i], BodyStr[i], &epmsg->head);
                curl_multi_add_handle(multi_handle, handles[i]);
            }
            else
            {
                printf("malloc failed\n");
                success = 0;
                break;
            }
                
            //printf("body: %s\n", DevPnameStr[i]);
        }   
   
        //
        if ( success )
        {
            curl_multi_perform(multi_handle, &running);
            do {
                int numfds;
                CURLMcode mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
              
                if ( mc != CURLM_OK ) {
                    LOG_E("curl_multi_wait() failed, code %d.\n", mc);
                    break;
                }
                curl_multi_perform(multi_handle, &running);
               
            } while (running);

            for (int i = 0; i < dev_count; i++) 
            {
                curl_multi_remove_handle(multi_handle, handles[i]);
                curl_easy_cleanup(handles[i]);
            }

        }
  
        _send_cond(epmsg);
    
        for(int i = 0; i < dev_count; i++)
        {
            free(BodyStr[i]);
        }

        curl_multi_cleanup(multi_handle);
        curl_global_cleanup();

        return dev_count;
    }
    else
    {
        printf("No device found\n");
        return 0;
    }
}



static void _envprodev_print_list(EnvProDev* dev)
{
    int count = 0;
    printf("_envprodev_print_list\n");
    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        EnvProNode* node;

        list_for_each_entry(node, &epmsg->head, list)
        {
            ++count;
            printf("(%d) --> Avg: %s\n", count, node->avg);
            //printf("(%d) --> Mean: %s\n", count, node->avg);
        }
    }
    else
    {
        printf("printable envprodev is NULL\n");
    }
}


static void _envprodev_condition(EnvProDev* dev)
{
    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;

        pthread_mutex_lock(&epmsg->pmutex);
    //printf("Condition variable is waiting\n");
        while ( !epmsg->complete )
            pthread_cond_wait(&epmsg->pcond, &epmsg->pmutex);
    
        pthread_mutex_unlock(&epmsg->pmutex);
    }
    
}

//static struct list_head* Current = NULL;
static void _envprodev_begin(EnvProDev* dev)
{
    if (dev && dev->msg) 
    {   
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        epmsg->current = epmsg->head.next;
    }
}

static EnvProNode* _envprodev_current(EnvProDev* dev)
{
    EnvProNode* current = NULL;

    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        current = list_entry(epmsg->current, EnvProNode, list);
    }
    return current;
}

static int _envprodev_is_end(EnvProDev* dev)
{
    int ret = 0;

    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        ret = (epmsg->current == &epmsg->head) ? 1 : 0;

        if ( ret )
            epmsg->complete = 0;
    }
    return ret;
}

static void _envprodev_next(EnvProDev* dev)
{
    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        epmsg->current = epmsg->current->next;
    }
}


static char* _envprodev_avg_start_time(EnvProDev* dev)
{
    return ((dev && dev->msg) ? ((EPPrivMsg*)dev->msg)->aTime : NULL);
}

static void _free_list(struct list_head * head)
{
    EnvProNode* node = NULL;
    EnvProNode* temp = NULL;
    int count = 0;
    
    list_for_each_entry(node,  head, list)
    {
        free(temp);
        free(node->avg);
        //free(node->mean);
        temp = node;
        count++;
    }

    free(temp);

    printf("ENPRODEV: free EnvProNode count: %d\n", count);
}

static void _destroy_list(EnvProDev* dev)
{
    if ( dev && dev->msg )
    {
        EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
        _free_list(&epmsg->head);
        INIT_LIST_HEAD(&epmsg->head);
    }
}

EnvProDev* envprodev_create(void)
{
    EnvProDev* dev = (EnvProDev*)malloc(sizeof(EnvProDev));

    if ( dev )
    {
        memset(dev, 0, sizeof(EnvProDev));
        EPPrivMsg* msg = (EPPrivMsg*)malloc(sizeof(EPPrivMsg));

        if ( msg )
        {
            memset(msg, 0, sizeof(EPPrivMsg));
            INIT_LIST_HEAD(&msg->head);
            msg->aTime = (char*)malloc(32);
            msg->nTime = (char*)malloc(32);
            msg->httpurl = (char*)malloc(200);
            msg->space = (char*)malloc(128);
            msg->current = NULL;
            if ( msg->aTime && msg->nTime && msg->httpurl && msg->space )
            {
                int ret = 0;
                ret = pthread_cond_init(&msg->pcond, NULL);
                ret += pthread_mutex_init(&msg->pmutex, NULL);
                if ( !ret )
                {
                    LOG_E("pthread_cond_t or pthread_mutex_t init failed\n");
                }
                msg->complete = 0;
                dev->msg = msg;

                dev->init = _enprodev_init;
                dev->request = _enprodev_request_datat;
                dev->print = _envprodev_print_list;
                dev->condition = _envprodev_condition;
                dev->begin = _envprodev_begin;
                dev->current = _envprodev_current;
                dev->is_end = _envprodev_is_end;
                dev->next = _envprodev_next;
                dev->destroy_list = _destroy_list;
                dev->avg_stime = _envprodev_avg_start_time;
            
            }
            else
            {
                free(msg->aTime);
                free(msg->nTime);
                free(msg->httpurl);
                free(msg->space);
                free(msg);
                free(dev);
                dev = NULL;
            }
            
        }
    }

    return dev;
}



EnvProDev* envprodev_destroy(EnvProDev* dev)
{
    if ( dev )
    {
        if ( dev->msg )
        {
            EPPrivMsg* epmsg = (EPPrivMsg*)dev->msg;
            pthread_mutex_destroy(&epmsg->pmutex);
            pthread_cond_destroy(&epmsg->pcond);
            free(epmsg->aTime);
            free(epmsg->nTime);
            free(epmsg->httpurl);
            free(epmsg->space);
            curl_slist_free_all(epmsg->header);
            _free_list(&epmsg->head);
            free(epmsg);
            epmsg = NULL;
        }

        free(dev);
        dev = NULL;
    }

    return dev;
}


static char* simdatbuf = "a99054-Avg=0.0515;a21028-Avg=20.7146;a21001-Avg=4000.0000;a19002-Avg=0.0000;a99054-Avg=0.3879;a34004-Avg=51.1373;a01002-Avg=207.1000;a21028-Avg=9.6332;a01006-Avg=0.0000;a01007-Avg=5.9383;a34002-Avg=72.1511;a01007-Avg=1.8350;a01008-Avg=109.8000;a21001-Avg=617.4701;a21004-Avg=13.3379;a01002-Avg=84.2200;a01006-Avg=1020.6213;a01001-Avg=20.7000;a21005-Avg=0.6604;a05024-Avg=160.1802;a01001-Avg=24.8575;a21026-Avg=889.0229;a01008-Avg=207.0000;a19002-Avg=0.0000;";

static char* _str_parser(char* str, char end, int* length)
{
    char* ret = NULL;

    if ( str )
    {
        char* strend = strchr(str, end);
        if ( strend )
        {
            int len = strend - str;
           
            if ( len > 0 )
            {
                ret = (char*)malloc(len + 2);
                strncpy(ret, str, len+1);
                ret[len+1] = 0;
                *length = len+1;
            }
            else
                *length = 0;
            
        }
    }

    return ret;
}

void envprodev_simu_request(EnvProDev* dev)
{
    int len = 0;
    int pos = 0;
    char* str = NULL;

    if ( dev )
    {
        char ntime[32];
        char atime[32];
        _local_time(ntime, atime);
        //printf("ntime is %s\n", ntime);
        //printf("atime is %s\n", atime);
        if ( ((EPPrivMsg*)dev->msg)->nTime && ((EPPrivMsg*)dev->msg)->aTime )
        {
            strcpy(((EPPrivMsg*)dev->msg)->nTime, ntime);
            strcpy(((EPPrivMsg*)dev->msg)->aTime, atime);
        }
        else
        {
            printf("nTime or aTime is NULL\n");
            
        }


        while ( (str = _str_parser(simdatbuf+pos, ';', &len)) != NULL )
        {
            EnvProNode* node = (EnvProNode*)malloc(sizeof(EnvProNode));
            node->avg = str;
            node->len = len;
            pos += len;

            list_add_tail(&node->list, &((EPPrivMsg*)dev->msg)->head);
            //printf("Request: %s   %d\n", str, len);
        }

        _send_cond((EPPrivMsg*)dev->msg);
    }
    
    
    printf("\n");
}